package com.goldcn.file.service;


import com.goldcn.common.exception.ServiceException;
import com.goldcn.common.util.CommonUtils;
import com.goldcn.common.util.UuidUtils;
import com.goldcn.config.FileConfig;
import com.goldcn.enumeration.ErrorCodeEnum;
import com.goldcn.file.util.CommonTools;
import com.goldcn.file.util.FileTypeJudge;
import com.goldcn.file.util.ImageUtil;
import com.goldcn.file.vo.FileVo;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSInputFile;
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

/**
 * Created by kylin.woo on 3/4/2016.
 */
@Service
@Transactional
public class UploadService {

    private Logger logger = Logger.getLogger(UploadService.class);

    private static final String temp_path = System.getProperty("user.dir") + File.separator + "image" + File.separator;

    @Autowired
    MongoTemplate template;

    @Autowired
    RestTemplate restTemplate;

    @Autowired
    FileConfig fileConfig;


    /**
     * 上传文件
     *
     * @param file
     */
    public FileVo FileProcessing(MultipartFile file, String type) {
        FileVo vo = new FileVo();
        try {
            //验证文件类型
            verifyFileType(file.getInputStream());
            //验证文件大小
            verifyFileSize(file.getSize());
            //系统指定原图文件名 加入上传待处理List集合
            List<String> filepaths = new ArrayList<String>();
            String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
            String fileName = UuidUtils.uuid() + suffix;
            String filePath = temp_path + fileName;

            filepaths.add(filePath);
            if ("1".equals(type)) {
                //文件上传前处理
                this.saveLoaclFolder(file, filePath);
                filepaths.addAll(uploadFilesBefore(filePath));
                //批量上传mogodb
                batchUploadFile(filepaths);
                //删除临时文件夹下的文件
                deteleTempFile(temp_path);
            } else {
                //原图上传
                uploadFile(file, fileName);
            }
            //返回原图信息
            vo.setFileName(fileName);
            vo.setFileSize(file.getSize());
            vo.setFileSuffix(suffix);
            vo.setFileUploadName(file.getOriginalFilename());
            return vo;
        } catch (ServiceException se) {
            vo.setErrorCode(se.getErrorCode());
            vo.setErrorDesc(se.getMessage());
        } catch (Exception e) {
            logger.error(e.getMessage());
            vo.setErrorCode(fileConfig.getErrorCode() + ErrorCodeEnum.otherError.getErrorCode());
            vo.setErrorDesc(ErrorCodeEnum.otherError.getErrorMsg());
        }
        return vo;
    }


    /**
     * 下载文件 原图
     *
     * @param response
     * @param fileName
     */
    public void downLoad(HttpServletResponse response, String fileName) {
        try {
            GridFS fs = new GridFS(template.getDb());
            GridFSDBFile dbFile = fs.findOne(fileName);

            if (dbFile == null) {
                String fileName_1 = isGoodsImg(fileName);
                if (fileName_1 != null)
                    dbFile = fs.findOne(fileName_1);
            }
            response.setContentLengthLong(dbFile.getLength());

            DateFormat df = new SimpleDateFormat(
                    "EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
            Date uploadDate = dbFile.getUploadDate();
            String lastModified = df.format(uploadDate);
            response.setContentType("image/*");
            long maxAge = 60L * 60L;
            response.setHeader("Cache-Control", "max-age=" + maxAge);
            response.setHeader("Last-Modified", lastModified);
            response.setDateHeader("Expires", uploadDate.getTime() + maxAge
                    * 1000L);

            dbFile.writeTo(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 下载文件
     *
     * @param response
     * @param fileName
     */
    public void downLoad(HttpServletResponse response, String fileName, String measureType) {
        try {
            String targetName = getFileName(fileName, measureType);
            GridFS fs = new GridFS(template.getDb());
            GridFSDBFile dbFile = fs.findOne(targetName);
            //无规格尺寸图 下载原图
            if (dbFile == null) {
                dbFile = fs.findOne(fileName);
            }
            response.setContentLengthLong(dbFile.getLength());

            DateFormat df = new SimpleDateFormat(
                    "EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
            Date uploadDate = dbFile.getUploadDate();
            String lastModified = df.format(uploadDate);
            response.setContentType("image/*");
            long maxAge = 60L * 60L;
            response.setHeader("Cache-Control", "max-age=" + maxAge);
            response.setHeader("Last-Modified", lastModified);
            response.setDateHeader("Expires", uploadDate.getTime() + maxAge
                    * 1000L);

            dbFile.writeTo(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 将原图保存在临时文件夹
     *
     * @return
     */
    public File saveBase64LoaclFolder(String base64Code, String orientation) {
        FileOutputStream out = null;
        try {
            base64Code = base64Code.substring(base64Code.indexOf(",") + 1);
            Base64 base64 = new Base64();
            byte[] bytes = base64.decodeBase64(new String(base64Code).getBytes());
            ByteArrayInputStream in = new ByteArrayInputStream(bytes);
            String filetype = FileTypeJudge.getType(in).toString().toUpperCase();
            String fileName = UuidUtils.uuid() + "." + filetype;
            in = new ByteArrayInputStream(bytes);
            if (verifyFileType(filetype)) {
                throw CommonTools.createException(ErrorCodeEnum.imageTypeError);
            }

            logger.info(bytes.length);
            byte[] buffer = new byte[1024];
            creatLocalFolder();
            out = new FileOutputStream(temp_path + File.separator + fileName);
            int bytesum = 0;
            int byteread = 0;
            while ((byteread = in.read(buffer)) != -1) {
                if (bytesum > 2 * 1024 * 1024) {
                    throw CommonTools.createException(ErrorCodeEnum.imageTooLarge);
                }
                bytesum += byteread;
                out.write(buffer, 0, byteread); // 文件写操作
                out.flush();
            }
            logger.info(bytesum);

            if (CommonUtils.isNotEmpty(orientation) && !orientation.equals("undefined")) {
                ImageUtil.rotateImg(temp_path + fileName, orientation, null);
            }

            return new File(temp_path + File.separator + fileName);
        } catch (ServiceException se) {
            throw se;
        } catch (Exception e) {
            throw CommonTools.createException(ErrorCodeEnum.saveLocalFolderFailure);
        } finally {
            try {
                if (out != null)
                    out.close();
            } catch (Exception e) {

            }
        }
    }

    /**
     * 上传mongoDB前操作
     *
     * @param filePath
     * @return
     */
    @Transactional
    private synchronized List<String> uploadFilesBefore(String filePath) {
        //生成3种规格的图片文件 返回文件路径
        return creatMultipleMeasure(filePath.substring(filePath.lastIndexOf(File.separator) + 1), temp_path);
    }

    /**
     * 验证文件类型
     *
     * @param fileInputStream 文件流
     */
    private void verifyFileType(InputStream fileInputStream) {
        try {
            String sourceType = FileTypeJudge.getType(fileInputStream).toString();
            if (!CommonTools.limitFileType(sourceType, fileConfig.getPermitType())) {
                throw CommonTools.createException(ErrorCodeEnum.imageTypeError);
            }
        } catch (Exception e) {
            throw CommonTools.createException(ErrorCodeEnum.imageTypeError);
        }
    }

    /**
     * 判断是否为正确文件类型（JPG,GIF,PNG）是 true 否 false
     *
     * @return
     */
    private boolean verifyFileType(String filetype) {
        if (!CommonTools.limitFileType(filetype, fileConfig.getPermitType())) {
            throw CommonTools.createException(ErrorCodeEnum.imageTypeError);
        }
        return false;
    }

    /**
     * 验证文件大小
     *
     * @param fileSize 文件大小
     */
    private void verifyFileSize(Long fileSize) {
        if (fileSize > fileConfig.getPermitSize()) {
            throw CommonTools.createException(ErrorCodeEnum.imageTooLarge);
        }
    }

    /**
     * 获取本地存储图片文件夹 没有则创建
     */
    private void creatLocalFolder() {
//        String dir = System.getProperty("user.dir");
//        dir = dir + File.separator + "image";
        File f = new File(temp_path);
        if (!f.exists()) {
            f.mkdirs();
        }
    }

    /**
     * 将原图保存在临时文件夹
     *
     * @param filePath 原图文件路径
     * @param file     原图文件流
     * @return
     */
    private void saveLoaclFolder(MultipartFile file, String filePath) {
        FileOutputStream fos = null;
        try {
            this.creatLocalFolder();
            fos = new FileOutputStream(filePath);
            fos.write(file.getBytes());
        } catch (Exception e) {
            logger.info(e.getMessage());
            throw CommonTools.createException(ErrorCodeEnum.saveLocalFolderFailure);
        } finally {
            try {
                if (fos != null)
                    fos.close();
            } catch (Exception e) {

            }
        }
    }

    /**
     * 创建多重规格尺寸至本地 返回本地地址集(含原图)
     *
     * @param fileName  系统生成文件名称
     * @param temp_path 本地临时文件夹路径
     * @return
     */
    private List<String> creatMultipleMeasure(String fileName, String temp_path) {
        List<String> filepaths = new ArrayList<String>();
        String prefix = fileName.substring(0, fileName.lastIndexOf("."));
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        String[] types = fileConfig.getMeasureType().split(",");

        for (String type : types) {
            String path = creatTargetImage(temp_path, prefix, suffix, Integer.parseInt(type));
            if (CommonUtils.isEmpty(path)) {
                throw CommonTools.createException(ErrorCodeEnum.imageMeasureError);
            } else {
                filepaths.add(path);
            }
        }
        return filepaths;
    }

    /**
     * 生成目标规格图片
     *
     * @param temp_path 临时文件夹路径
     * @param prefix    前缀（文件名uuid）
     * @param suffix    后缀
     * @param type      规格
     * @return
     */
    private String creatTargetImage(String temp_path, String prefix, String suffix, int type) {
        String srcImgPath = temp_path + prefix + suffix;
        String outImgPath = temp_path + prefix + "_" + type + "x" + type + suffix;
        //指定长或者宽的最大值来压缩图片
        /*if(ImageUtil.compressImage(srcImgPath,outImgPath,type)){

            return outImgPath;
        }else{
            return "";
        }*/
        //将图片按照指定的图片尺寸压缩
        if (ImageUtil.compressImage(srcImgPath, outImgPath, type, type, false)) {
            return outImgPath;
        } else {
            return "";
        }
    }

    /**
     * 批量上传mongoDB
     *
     * @param filepaths 本地临时文件路径集合
     */
    @Transactional
    private void batchUploadFile(List<String> filepaths) {
        for (String path : filepaths) {
            try {
                uploadFile(path);
            } catch (Exception e) {
                batchDeleteFile(filepaths);//将插入至mongo的图片删除
                logger.info(e.getMessage());
                throw CommonTools.createException(ErrorCodeEnum.batchUploadFailure);
            }
        }
    }

    /**
     * 上传mongoDB 单个上传
     *
     * @param path
     * @throws Exception
     */
    private void uploadFile(String path) throws Exception {
        InputStream is = null;
        try {
            File file = new File(path);
            String name = file.getName();
            is = new FileInputStream(file);
            GridFS gfsPhoto = new GridFS(template.getDb());
            GridFSInputFile gfsFile = gfsPhoto.createFile(is);
            gfsFile.setFilename(name);
            gfsFile.save();
        } finally {
            try {
                if (is != null)
                    is.close();
            } catch (Exception e) {
                logger.info(e.getMessage());
            }
        }
    }

    /**
     * 上传mongoDB 单个上传
     *
     * @param file
     * @param fileName
     * @throws Exception
     */
    private void uploadFile(MultipartFile file, String fileName) throws Exception {
        GridFS gfsPhoto = new GridFS(template.getDb());
        GridFSInputFile gfsFile = gfsPhoto.createFile(file.getInputStream());
        gfsFile.setFilename(fileName);
        gfsFile.save();
    }

    /**
     * 根据文件名称集合删除mongo中的文件集
     *
     * @param filepaths
     */
    private void batchDeleteFile(List<String> filepaths) {
        for (String path : filepaths) {
            GridFS gfsPhoto = new GridFS(template.getDb());
            File file = new File(path);
            String name = file.getName();
            if (gfsPhoto.find(name).size() > 0) {
                gfsPhoto.remove(name);
            }
        }
    }

    /**
     * 删除临时文件夹下的文件
     */
    public void deteleTempFile() {
        deteleTempFile(temp_path);
    }

    /**
     * 删除临时文件夹下的文件
     *
     * @param temp_path
     */
    private void deteleTempFile(String temp_path) {
        File folder = new File(temp_path);
        if (!(folder.isFile() || folder.listFiles().length == 0)) {
            File[] files = folder.listFiles();
            for (int i = 0; i < files.length; i++) {
                deteleTempFile(files[i].getPath());
                files[i].delete();
            }
        }
    }

    /**
     * 判断是否为商品图片
     *
     * @param fileName 源文件名
     * @return 原图
     */
    private String isGoodsImg(String fileName) {
        String prefix = fileName.substring(0, fileName.lastIndexOf("."));
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        String prefix_1 = prefix.substring(prefix.lastIndexOf("_"));
        if ("_134x134".equals(prefix_1) || "_350x350".equals(prefix_1) || "_414x414".equals(prefix_1)) {
            return prefix.substring(0, prefix.lastIndexOf("_")) + suffix;
        } else {
            return null;
        }
    }


    /**
     * 获取下载指定规格 文件名
     *
     * @param originalFileName 源文件
     * @param measureType      需求尺寸
     * @return
     */
    private String getFileName(String originalFileName, String measureType) {
        String[] measures = fileConfig.getMeasureType().split(",");
        Integer index = Integer.parseInt(measureType);
        if (index > 0 && index <= measures.length) {
            String prefix = originalFileName.substring(0, originalFileName.lastIndexOf("."));
            String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));

            if (CommonUtils.isNotEmpty(measures[index - 1])) {
                return prefix + "_" + measures[index - 1] + "x" + measures[index - 1] + suffix;
            } else {
                return originalFileName;
            }
        } else {
            throw new ServiceException("参数错误");
        }
    }

}